package ac.zlf.springmybatis.core.mybatis.dialect;

import ac.zlf.springmybatis.core.mybatis.domain.PageRequest;
import ac.zlf.springmybatis.core.mybatis.util.SQLHelper;
import ac.zlf.springmybatis.core.mybatis.domain.Sort;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMapping.Builder;

public class Dialect {
    protected final Log logger = LogFactory.getLog(getClass());
    protected MappedStatement mappedStatement;
    protected PageRequest pageRequest;
    protected Object parameterObject;
    protected BoundSql boundSql;
    protected List<ParameterMapping> parameterMappings;
    protected Map<String, Object> pageParameters = new HashMap();
    private String pageSQL;
    private String countSQL;

    public Dialect(MappedStatement mappedStatement, Object parameterObject, PageRequest pageBounds) {
        this.mappedStatement = mappedStatement;
        this.parameterObject = parameterObject;
        this.pageRequest = pageBounds;

        init();
    }

    protected void init() {
        this.boundSql = this.mappedStatement.getBoundSql(this.parameterObject);
        this.parameterMappings = new ArrayList(this.boundSql.getParameterMappings());
        if ((this.parameterObject instanceof Map)) {
            this.pageParameters.putAll((Map) this.parameterObject);
        } else {
            for (ParameterMapping parameterMapping : this.parameterMappings) {
                this.pageParameters.put(parameterMapping.getProperty(), this.parameterObject);
            }
        }
        StringBuffer bufferSql = new StringBuffer(this.boundSql.getSql().trim());
        if (bufferSql.lastIndexOf(";") == bufferSql.length() - 1) {
            bufferSql.deleteCharAt(bufferSql.length() - 1);
        }
        String sql = SQLHelper.getLineSql(bufferSql.toString());
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("原始SQL: " + sql);
        }
        this.pageSQL = sql;
        if (this.pageRequest.getSort() != null) {
            this.pageSQL = getSortString(sql, this.pageRequest.getSort());
        }
        this.pageSQL = getLimitString(this.pageSQL, "__offset", this.pageRequest.getOffset(), "__limit", this.pageRequest.getPageSize());
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("分页SQL: " + this.pageSQL);
        }
        this.countSQL = getCountString(sql);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("总条数SQL: " + this.countSQL);
        }
    }

    public List<ParameterMapping> getParameterMappings() {
        return this.parameterMappings;
    }

    public Object getParameterObject() {
        return this.pageParameters;
    }

    public String getPageSQL() {
        return this.pageSQL;
    }

    protected void setPageParameter(String name, Object value, Class type) {
        ParameterMapping parameterMapping = new ParameterMapping.Builder(this.mappedStatement.getConfiguration(), name, type).build();
        this.parameterMappings.add(parameterMapping);
        this.pageParameters.put(name, value);
    }

    public String getCountSQL() {
        return this.countSQL;
    }

    protected String getLimitString(String sql, String offsetName, int offset, String limitName, int limit) {
        throw new UnsupportedOperationException("paged queries not supported");
    }

    protected String getCountString(String sql) {
        return SQLHelper.getCountString(sql);
    }

    protected String getSortString(String sql, Sort sort) {
        if (sort == null) {
            return sql;
        }
        int orderIndex = SQLHelper.getLastOrderInsertPoint(sql);
        StringBuffer sb = new StringBuffer(sql.substring(0, orderIndex) + "order by ");
        Iterator<Sort.Order> orderIt = sort.iterator();
        while (orderIt.hasNext()) {
            Sort.Order order = (Sort.Order) orderIt.next();
            sb.append(String.format("%s %s, ", new Object[]{order.getProperty(), order.getDirection()}));
        }
        sb.delete(sb.length() - 2, sb.length());
        return sb.toString();
    }
}